//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <bsp.h>
#include <mmu.h>
#include <init.h>
#include "vga.h"

const byte_t c_bDefaultTextColor =
        TEXT_COLOR(TextColor_Black, TextColor_White);
const uint_t c_uTabStopValue     = 8;
const textchar_t c_tcTab         =
        TEXT_CHAR(' ', TEXT_COLOR(TextColor_Black, TextColor_Black));

STATIC uint_t s_uCursorLocation = 0;

EXTERN_C void PrintComPort(const char *szMessage);

INLINE void SetCursorLocation(uint_t uCursorLocation)
{
    Outb(VGAPort_AddressRegister, VGARegIndex_CursorLocationLow);
    Outb(VGAPort_DataRegister,    LOBYTE(uCursorLocation));
    Outb(VGAPort_AddressRegister, VGARegIndex_CursorLocationHigh);
    Outb(VGAPort_DataRegister,    HIBYTE(uCursorLocation));
}

STATIC void CDECL BspClearScreen();

STATIC void CDECL BspPrintScreen(const char *szMessage);

EXTERN_C DECL_CODEINIT bool_t CDECL InitConsole()
{
    BspClearScreen();
    return BspSetupComPort(COMPORT_PRINT, NULL);
}

STATIC void CDECL BspClearScreen()
{
    textchar_t *pTextChars =
        (textchar_t *)VIRTADDR(c_paVGATextVideoMemoryPhysBase);

    for (uint_t i = 0; i < TextScreen_Columns * TextScreen_Rows; i++) {
        pTextChars[i] = TEXT_CHAR(' ', c_bDefaultTextColor);
    }

    s_uCursorLocation = 0;
    SetCursorLocation(s_uCursorLocation);
}

EXTERN_C void CDECL BspWriteConsole(const char *szMessage)
{
    if (NULL == szMessage) return;

    BspPrintScreen(szMessage);
    PrintComPort(szMessage);
}

EXTERN_C void PrintComPort(const char *szMessage)
{
    assert(szMessage);

    for (const char *pc = szMessage; '\0' != *pc; pc++) {
        BspWriteComPort(COMPORT_PRINT, *pc);
    }
}

STATIC void CDECL BspPrintScreen(const char *szMessage)
{
    assert(szMessage);

    textchar_t *pTextChars =
        (textchar_t *)VIRTADDR(c_paVGATextVideoMemoryPhysBase);

    for (const char *pc = szMessage; '\0' != *pc; pc++) {
        switch (*pc) {
            case '\a':
                BspBeep();
                break;

            case '\b':
                do {
                    if (0 == s_uCursorLocation) break;

                    s_uCursorLocation--;
                    pTextChars[s_uCursorLocation] =
                    TEXT_CHAR(' ', c_bDefaultTextColor);
                } while (c_tcTab == pTextChars[s_uCursorLocation - 1]
                    && 0 != (s_uCursorLocation % c_uTabStopValue));
            // why delete align with 8? It's not the vgs's job.

                break;

            case '\n':
                s_uCursorLocation += TextScreen_Columns;
            // no break statement! Need translate LF to CRLF.

            case '\r':
                s_uCursorLocation -= s_uCursorLocation % TextScreen_Columns;
                break;

            case '\t':
                do {
                    pTextChars[s_uCursorLocation] = c_tcTab;
                    s_uCursorLocation++;
                } while (s_uCursorLocation % c_uTabStopValue);
                break;

            default:
                pTextChars[s_uCursorLocation] =
                TEXT_CHAR(*pc, c_bDefaultTextColor);
                s_uCursorLocation++;
        } // switch

        // Need Scroll ?
        if (s_uCursorLocation >= TextScreen_Columns * (TextScreen_Rows - 1)) {
            memcpy(pTextChars, pTextChars + TextScreen_Columns,
                    sizeof(textchar_t) * TextScreen_Columns
                        * (TextScreen_Rows - 1));

            for (uint_t i = TextScreen_Columns * (TextScreen_Rows - 1);
                i < TextScreen_Columns * TextScreen_Rows; i++) {
                pTextChars[i] = TEXT_CHAR(' ', c_bDefaultTextColor);
            }

            s_uCursorLocation -= TextScreen_Columns;
        }
    }

    SetCursorLocation(s_uCursorLocation);
}
